#include "MuPDFManager.h"
#include <cmath>




// init
MuPDFManager::MuPDFManager(int width,int height)
{
    char temp_log[100]={0,};
    logger = new Logger("MuPDFManager.txt");

    // init variables;
    filename = NULL;
    doctitle = NULL;

    xref = NULL;
    outline = NULL;
    pages_tree = NULL;
    page = NULL;
    rast = NULL;
   	obj = NULL;
	image = NULL;
    current_page_num = 0;
    page_num = 0;

    zoom = 0.0f;
    rotate = 0;
    shrinkwrap = 0;
    panx = 0;
    pany = 0;

    
    fz_error error;
    
    fz_cpudetect();
    fz_accelerate();
    
    // Renderer Init
    error = fz_newrenderer(&rast, pdf_devicergb, 0, width*height);
    
    if(error){
        logger->printLog("fz_newrenderer Failed");
    }else{
        logger->printLog("fz_newrenderer Success");
    }
}


MuPDFManager::~MuPDFManager()
{
    
    
}

// open file
int MuPDFManager::openPDFFile(char* filename)
{
    char temp_log[100]={0,};
    fz_error error;
    
    error = pdf_newxref(&xref);
    if(error){
        logger->printLog("pdf_newxref Failed");
    }else{
        logger->printLog("pdf_newxref Success");
    }    
    
    error = pdf_loadxref(xref, filename);
    if(error){
        logger->printLog("pdf_loadxref Failed");
    }else{
        logger->printLog("pdf_loadxref Success");
    }    
    
    error = pdf_decryptxref(xref);
    if(error){
        logger->printLog("pdf_decryptxref Failed");
    }else{
        logger->printLog("pdf_decryptxref Success");
    }    
    
    if(xref->crypt){
        logger->printLog("Encrypted Document");
    }else{
        logger->printLog("Not Encrypted Document");
    }
    
    
	/*
	 * Load page tree
	 */
	error = pdf_loadpagetree(&pages_tree, xref);
    if(error){
        page_num = -1;
        logger->printLog("pdf_loadpagetree Failed");
        goto out;
    }else{
        logger->printLog("pdf_loadpagetree Success");
        page_num = pdf_getpagecount(pages_tree);
        sprintf(temp_log,"page num : %d",page_num);
        logger->printLog(temp_log);
    }


	/*
	 * Load meta information
	 */    
    obj = fz_dictgets(xref->trailer, "Root");
    if(!obj){
        logger->printLog("syntaxerror: missing Root object");        
    }else{
        logger->printLog("get Root object Success");        
    }

	error = pdf_loadindirect(&xref->root, xref, obj);
    if(error){
        logger->printLog("pdf_loadindirect Failed");
    }else{
        logger->printLog("pdf_loadindirect Success");
    }
    
    
	obj = fz_dictgets(xref->trailer, "Info");
	if (obj)
	{
		error = pdf_loadindirect(&xref->info, xref, obj);
        if(error){
            logger->printLog("pdf_loadindirect Failed");
        }else{
            logger->printLog("pdf_loadindirect Success");
        }
	}

	error = pdf_loadnametrees(xref);
    if(error){
        logger->printLog("pdf_loadnametrees Failed");
    }else{
        logger->printLog("pdf_loadnametrees Success");
    }

	error = pdf_loadoutline(&outline, xref);
    if(error){
        logger->printLog("pdf_loadoutline Failed");
    }else{
        logger->printLog("pdf_loadoutline Success");
    }

	doctitle = filename;
	if (strrchr(doctitle, '\\'))
		doctitle = strrchr(doctitle, '\\') + 1;
	if (strrchr(doctitle, '/'))
		doctitle = strrchr(doctitle, '/') + 1;
	if (xref->info)
	{
		obj = fz_dictgets(xref->info, "Title");
		if (obj)
		{
			error = pdf_toutf8(&doctitle, obj);
            if(error){
                logger->printLog("pdf_loadoutline Failed");
            }else{
                logger->printLog("pdf_loadoutline Success");
            }
		}
	} 
	
	logger->printLog(doctitle);
	
	current_page_num = 1;

    // view setting
	shrinkwrap = 1;
	if (zoom <= 0.0)
		zoom = 1.0;
	rotate = 0;
	panx = 0;
	pany = 0; 
 
out:
    return page_num;
}

// close file
void MuPDFManager::closePDFFile()
{
    
    
}

int MuPDFManager::getTotalPageNum()
{
    return page_num;
}

GLuint* MuPDFManager::loadImage(int page_num, int* imageWidth,int* imageHeight, int* textureWidth, int* textureHeight)
{
    
    char temp_log[100]={0,};
    unsigned char* bitmap_data;


    fz_error error;
	fz_matrix ctm;
	fz_rect bbox;
    
        
	GLuint* ID=new GLuint[1];
	glGenTextures( 1, &ID[0] );

	glBindTexture( GL_TEXTURE_2D, ID[0] );

    // Firt Time Check???
    
    // Get PDF Page using muPDF size : imageWidht, imageHeight
    // same as "load page" of mupdf APP
    // 1. goto page
    // 2. load page

	if (page)
		pdf_droppage(page);
	page = 0;

	obj = pdf_getpageobject(pages_tree, page_num - 1);

	error = pdf_loadpage(&page, xref, obj);
    if(error){
        logger->printLog("pdf_loadpage Failed");
    }else{
        logger->printLog("pdf_loadpage Success");
    }
 
    // get pixmap
	if (1)
	{
		if (image)
			fz_droppixmap(image);
		image = 0;
		
        //ctm = pdfapp_viewctm(app);
        {
            ctm = fz_identity();
            ctm = fz_concat(ctm, fz_translate(0, -page->mediabox.y1));
            ctm = fz_concat(ctm, fz_scale(zoom, -zoom));
            ctm = fz_concat(ctm, fz_rotate(rotate + page->rotate));
        }
        
		bbox = fz_transformaabb(ctm, page->mediabox);
		error = fz_rendertree(&image, rast, page->tree, ctm, fz_roundrect(bbox), 1);
		
		
        if(error){
            logger->printLog("fz_rendertree Failed");
        }else{
            logger->printLog("fz_rendertree Success");
        }
        //winconvert(image);
	}
    
    sprintf(temp_log,"image width : %d height : %d, x : %d, y : %d, n : %d",image->w,image->h,image->x,image->y,image->n);
    logger->printLog(temp_log);
    
    *imageWidth = image->w;
    *imageHeight =image->h;
    
    bitmap_data = (unsigned char*)malloc(3*image->w*image->h);
    
#if 1
    // swap bits
    for(int i = 0 ; i < image->h ; i++){
        for(int j = 0 ; j < image->w ; j++){
            bitmap_data[3*(i*image->w+j)] = image->samples[4*(i*image->w+j)+1];
            bitmap_data[3*(i*image->w+j)+1] = image->samples[4*(i*image->w+j)+2];
            bitmap_data[3*(i*image->w+j)+2] = image->samples[4*(i*image->w+j)+3];
        }
    }
#endif

	glPixelStorei(GL_UNPACK_ALIGNMENT,   1   );

	float power_of_two_that_gives_correct_width=std::log((float)(image->w))/std::log(2.0);
	float power_of_two_that_gives_correct_height=std::log((float)(image->h))/std::log(2.0);



#if 1
    // check if image dimensions are a power of two
    if( (int)power_of_two_that_gives_correct_width == power_of_two_that_gives_correct_width &&
        (int)power_of_two_that_gives_correct_height == power_of_two_that_gives_correct_height)
    {
            // note: must make a local copy before passing the data to OpenGL, as GetData() returns RGB 
            // and we want the Alpha channel if it's present. Additionally OpenGL seems to interpret the 
            // data upside-down so we need to compensate for that.
            //GLubyte *bitmapData=image->samples;
            GLubyte *bitmapData=(GLubyte *)bitmap_data;
            //GLubyte *alphaData=img->GetAlpha();
            GLubyte *imageData;

            //int bytesPerPixel = img->HasAlpha() ?  4 : 3;
            int bytesPerPixel = 3;

            int imageSize = (image->w) * (image->h) * bytesPerPixel;
            imageData=(GLubyte *)malloc(imageSize);

            int rev_val=(image->h)-1;

            for(int y=0; y<(image->h); y++)
            {
                    for(int x=0; x<(image->w); x++)
                    {
                            imageData[(x+y*(image->w))*bytesPerPixel+0]=
                                    bitmapData[( x+(rev_val-y)*(image->w))*3];

                            imageData[(x+y*(image->w))*bytesPerPixel+1]=
                                    bitmapData[( x+(rev_val-y)*(image->w))*3 + 1];

                            imageData[(x+y*(image->w))*bytesPerPixel+2]=
                                    bitmapData[( x+(rev_val-y)*(image->w))*3 + 2];

                    }//next
            }//next

            // if yes, everything is fine
            glTexImage2D(GL_TEXTURE_2D,
                         0,
                         bytesPerPixel,
                         image->w,
                         image->h,
                         0,
                         GL_RGB,
                         GL_UNSIGNED_BYTE,
                         imageData);

            (*textureWidth)  = (image->w);
            (*textureHeight) = (image->h);
            
            sprintf(temp_log,"width: %d, height : %d, bytesPerPixel : %d",image->w,image->h,bytesPerPixel);
            logger->printLog(temp_log);
            logger->printLog("Image is Power Of Two");
            free(imageData);
    }
    else // texture is not a power of two. We need to resize it
    {
    	
    	int newWidth=(int)std::pow( 2.0, (int)(std::ceil(power_of_two_that_gives_correct_width)) );
    	int newHeight=(int)std::pow( 2.0, (int)(std::ceil(power_of_two_that_gives_correct_height)) );
    	
    	//printf("Unsupported image size. Recommand values: %i %i\n",newWidth,newHeight);   
    	
        //GLubyte *bitmapData=(GLubyte *)image->samples;
        GLubyte *bitmapData=(GLubyte *)bitmap_data;
        //GLubyte *alphaData=img->GetAlpha();
        GLubyte *imageData;
    	
    	int old_bytesPerPixel = 3;
    	int bytesPerPixel = 3;
    	
    	int imageSize = newWidth * newHeight * bytesPerPixel;
    	imageData=(GLubyte *)malloc(imageSize);
    	
    	int rev_val=(image->h)-1;
    	
    	for(int y=0; y<newHeight; y++)
    	{
    		for(int x=0; x<newWidth; x++)
    		{
    			
    			if( x<(image->w) && y<(image->h) ){
    				imageData[(x+y*newWidth)*bytesPerPixel+0]=
    				bitmapData[( x+(rev_val-y)*(image->w))*old_bytesPerPixel + 0];
    				
    				imageData[(x+y*newWidth)*bytesPerPixel+1]=
    					bitmapData[( x+(rev_val-y)*(image->w))*old_bytesPerPixel + 1];
    				
    				imageData[(x+y*newWidth)*bytesPerPixel+2]=
    					bitmapData[( x+(rev_val-y)*(image->w))*old_bytesPerPixel + 2];
    				
    			}
    			else
    			{
    				
    				imageData[(x+y*newWidth)*bytesPerPixel+0] = 0;
    				imageData[(x+y*newWidth)*bytesPerPixel+1] = 0;
    				imageData[(x+y*newWidth)*bytesPerPixel+2] = 0;
    			}
    			
    		}//next
    	}//next
    	
    	
    	glTexImage2D(GL_TEXTURE_2D,
    				 0,
    				 3,
    				 newWidth,
    				 newHeight,
    				 0, 
    				 GL_RGB, 
    				 GL_UNSIGNED_BYTE,
    				 imageData);
    	
    	(*textureWidth)=newWidth;
    	(*textureHeight)=newHeight;
        sprintf(temp_log,"width: %d, height : %d, bytesPerPixel : %d",newWidth,newHeight,bytesPerPixel);
        logger->printLog(temp_log);		
        logger->printLog("Image is not Power Of Two");		
    	free(imageData);

    }
#endif

	free(bitmap_data);
	// set texture parameters as you wish
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // GL_LINEAR
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // GL_LINEAR
    
    return ID;            
            
}
